#include"stdafx.h"
#include "MIDIOutDevice.h"
#include "midi.h"


//--------------------------------------------------------------------
// Using declarations
//--------------------------------------------------------------------


using midi::CMIDIOutDevice;
using midi::CMIDIOutException;
// Constructs CMIDIOutDevice object in an closed state
CMIDIOutDevice::CMIDIOutDevice() :
m_State(CLOSED)
{

}

// Destruction
CMIDIOutDevice::~CMIDIOutDevice()
{
    // Close device
    Close();

}


// Opens the MIDI output device
void CMIDIOutDevice::Open(UINT DeviceId)
{
    // Makes sure the previous device, if any, is closed before 
    // opening another one
    Close();

    // Open MIDI output device
    MMRESULT Result = ::midiOutOpen(&m_DevHandle, DeviceId, 
                                 reinterpret_cast<DWORD>(MidiOutProc),
                                 reinterpret_cast<DWORD>(this),
                                 CALLBACK_FUNCTION);

    // If we are able to open the device, change state
    if(Result == MMSYSERR_NOERROR)
    {
        m_State = OPENED;

    }
    // Else opening failed, throw exception
    else
    {

        throw CMIDIOutException(Result);
    }
}


// Closes the MIDI output device
void CMIDIOutDevice::Close()
{
    // Only close an already opened device
    if(m_State == OPENED)
    {
        // Change state
        m_State = CLOSED;

         // Close the MIDI output device
        ::midiOutClose(m_DevHandle);
    }
}


// Sends short message
void CMIDIOutDevice::SendMsg(DWORD Msg)
{
    if(m_State == OPENED)
    {
        MMRESULT Result = ::midiOutShortMsg(m_DevHandle, Msg);

        if(Result != MMSYSERR_NOERROR)
        {
            throw CMIDIOutException(Result);
        }
    }
}

// Gets the capabilities of a particular MIDI output device
void CMIDIOutDevice::GetDevCaps(UINT DeviceId, MIDIOUTCAPS &Caps)
{
    MMRESULT Result = ::midiOutGetDevCaps(DeviceId, &Caps, 
                                          sizeof Caps);

    // If we are not able to retrieve device capabilities, throw 
    // exception
    if(Result != MMSYSERR_NOERROR)
    {
        throw CMIDIOutException(Result);
    }
}

// Called by Windows when a MIDI input event occurs
void CALLBACK CMIDIOutDevice::MidiOutProc(HMIDIOUT MidiOut, UINT Msg,
                                          DWORD_PTR Instance, DWORD_PTR Param1,
                                          DWORD_PTR Param2)
{
    CMIDIOutDevice *Device;
    
    Device = reinterpret_cast<CMIDIOutDevice *>(Instance);

    if(Msg == MOM_DONE)
    {
        ::SetEvent(Device->m_Event);
    }
}

